#include "rsuctrlforshineg60e.h"
#include <QTime>

RsuCtrlForShineG60E::RsuCtrlForShineG60E(QObject *parent) : BaseRsuCtrl(parent)
{
    connect(&m_rsuComm, &RsuCommForShineG60E::newFrameArrived,
            this, &RsuCtrlForShineG60E::processRsuFrame);
    connect(&m_rsuComm, &RsuCommForShineG60E::newFrameArrived,
            this, &RsuCtrlForShineG60E::newFrameArrived);
    connect(&m_rsuComm, &RsuCommForShineG60E::stateChanged,
            this, &RsuCtrlForShineG60E::socketStateChanged);

    m_nExpectedFrameType=0;

    m_pNoResponseTimer = new QTimer(this);
    // NOTE: 初始化指令、获取RSU信息指令耗时较长，需要设置较长的等待时间以免误判
    m_pNoResponseTimer->setInterval(10000);
    m_pNoResponseTimer->setSingleShot(true);
    connect(m_pNoResponseTimer, &QTimer::timeout,
            this, &RsuCtrlForShineG60E::handleNoResponseError);

    m_pHeartBeatTimer = new QTimer(this);
    m_pHeartBeatTimer->setInterval(10000);
    connect(m_pHeartBeatTimer, &QTimer::timeout,
            this, &RsuCtrlForShineG60E::handleHeartBeatTimeout);
}

RsuCtrlForShineG60E::~RsuCtrlForShineG60E()
{
    closeRsu();
}

/*!
 * \brief 打开天线
 */
void RsuCtrlForShineG60E::reopenRsu()
{
    if (m_sRsuHost.length()>0)
    {
        m_rsuComm.openRsu(m_sRsuHost, m_nRsuPort);
        m_pHeartBeatTimer->start();
    }
}

void RsuCtrlForShineG60E::closeRsu()
{
    m_pHeartBeatTimer->stop();
    m_rsuComm.closeRsu();
}

int RsuCtrlForShineG60E::initRsu()
{
    expectResult(0xB0);
    quint8 Time[16];
    fillCurrentTime(Time);
    return m_rsuComm.initRsuRq((char *)Time,
            m_bstInterval,
            m_rsuPower,
            m_rsuChannel);
}

void RsuCtrlForShineG60E::expectResult(int nFrameType)
{
    m_nExpectedFrameType=nFrameType;
    m_pNoResponseTimer->start();
}

void RsuCtrlForShineG60E::handleNoResponseError()
{
    if (0xB0==m_nExpectedFrameType)
    {
        // 超时重发初始化指令
        initRsu();
    }

    m_nNoResponseErrorCount++;
    if (m_nNoResponseErrorCount>2)
    {
        setStatus(DEV_STATUS_ERR);
    }

    if (m_nNoResponseErrorCount>5)
    {
        tryReconnect();
    }
}

void RsuCtrlForShineG60E::handleHeartBeatTimeout()
{
    qint64 currentTime=QDateTime::currentMSecsSinceEpoch();
    if (qAbs(currentTime-m_nDevOKTime)>65000)
    {
        setStatus(DEV_STATUS_ERR);
        tryReconnect();
    }
}

void RsuCtrlForShineG60E::socketStateChanged(QAbstractSocket::SocketState socketState)
{
    if (QAbstractSocket::ConnectedState==socketState)
    {
        initRsu();
    }
    else
    {
        setStatus(DEV_STATUS_ERR);
    }
}

void RsuCtrlForShineG60E::processRsuFrame(quint8 nFrameIndex, quint8 nFrameType, const QByteArray &frame)
{
    Q_UNUSED(nFrameIndex);

    if (nFrameType==m_nExpectedFrameType)
    {
        this->m_pNoResponseTimer->stop();
        m_nExpectedFrameType=0;
    }

    if (0xB0==nFrameType)
    {
        processRsuInitFrame(nFrameIndex, frame);
    }
    else if (0xB2==nFrameType)
    {
        processObuInitFrame(frame);
    }
}

void RsuCtrlForShineG60E::processRsuInitFrame(quint8 nFrameIndex, const QByteArray &frame)
{
    /*
    RSU设备状态信息（B0）帧

    FF 08 B0 04 00 00 00 00 00 00 00 00 00 00 00 00 09 02 13 DC 25 01 04 00 00 00 00 00 58 FF
       00 01 02 03                09                15 16 17       20    22             27
    00	1	RSCTL	串口帧序列号
    01	1	FrameType	数据帧类型标识，此处取值B0H
    02	1	RSUStatus	RSU主状态参数；0x00表示正常，否则表示异常；
    03	6	RSUTerminalId1	PSAM卡终端机编号（对应MF1卡）
    09	6	RSUTerminalId2	PSAM卡终端机编号（对应CPU卡）
    15	1	RSUAlgId	算法标识（对应CPU卡的交易类型）
    16	1	RSUManuID	RSU厂商代码，0x01表示埃特斯公司，0x02表示金溢公司，0x03表示聚利公司，0x04表示首信公司。05东海  08万集
    17	3	RSUIndividualID	RSU编号
    20	2	RSUVersion	RSU软件版本号
    22	5	Reserved	保留字节。
    27	1	BCC	异或校验值
    帧信息描述：
    (1) RSU在上电或收到PC初始化指令后发送该帧信息给PC；
    PC应答：
    (1)	如果RSCTL等于0x98，表示RSU刚刚上电，PC应发送初始化指令(C0指令)到RSU，作为对收到信息的应答，同时设置RSU的工作参数；
    (2) 如果RSCTL不等于0x98，PC则应答继续交易指令(C1指令)；

    */

    if (frame.length()<25)
    {
        logger()->error(tr("error rsu init frame[%1]")
                        .arg(frame.toHex().data()));
        return;
    }
    int nRsuStatus=frame[0];
    QByteArray baRsuInfo=frame.mid(15, 6);
    memcpy(m_BeaconId, baRsuInfo.data(), 4);
    setStatus(DEV_STATUS_OK);
    if (0x98==nFrameIndex)
    {
        // Note: 对于网络接口设备，测试不到这种情况
        initRsu();
    }
    else
    {
        m_rsuComm.sendCmd(0xC1, 0);
    }
    emit rsuInited(nRsuStatus, baRsuInfo);
}

void RsuCtrlForShineG60E::processObuInitFrame(const QByteArray &frame)
{
    /*
    OBU系统信息(B2)帧

    FF 48 B2 00 00 00 00 80 7A FF
    FF 08 B2 02 CA 21 2F 00 C9 BD B6 AB 37 01 00 01 01 00 37 01 02 14 20 79 81 05 20 14 10 28 21 14 10 28 10 82 00 4D FF
       00 01 02          06 07                      15 16 17                      25          29          33 34    36

    00	1	RSCTL	串口帧序列号
    01	1	FrameType	数据帧类型标识，此处取值B2H
    02	4	OBUID	OBU号
    06	1	ErrorCode	OBU执行状态，取值为00H，才有后续数据内容。
    07	8	ContractProvider	发行商代码
    15	1	ContractType   	协约类型，暂取1
    16	1	ContractVersion  	协约版本，暂取1
    17	8	ContractSerialNumber	应用序列号，8个字节，BCD编码
    25	4	ContractSignedDate	协议签署日期，yyyymmdd (BCD编码)
    29	4	ContractExpiredDate	协议过期日期，yyyymmdd （BCD编码）
    33	1	Equitmentstatus	OBU硬件版本
    34	2	OBUStatus	OBU状态
    36	1	BCC	异或校验值

    帧信息描述：
    1. RSU在搜索到OBU号后发送OBU号给PC，表示通讯区域内存在此OBU，同时ErrorCode为零；
    2. RSU在其通信区域内始终搜索不到OBU的情况下，也需要定时向车道控制器发送此帧，
       作为心跳信息使用，表示天线正常工作状态，同时ErrorCode非零，取值为0x80，OBUID取值0x00,0x00,0x00,0x00。
       频率为1分钟。

    PC应答：
    PC应答C1指令，若为心跳信号则PC可以不应答。

    */

    if (frame.length()<5)
    {
        logger()->error(tr("error b2 frame[%1]")
                        .arg(frame.toHex().data()));
        return;
    }

    quint8 nErrorCode=frame[4];
    if (frame.length()<33)
    {
        if (0x80==nErrorCode)
        {
            setStatus(DEV_STATUS_OK);
        }
        else
        {
            logger()->error(tr("error b2 frame[%1]")
                            .arg(frame.toHex().data()));
        }
    }
    else
    {
        RawObuConfiguration obuCfg;
        memset(&obuCfg, 0, sizeof(obuCfg));
        memcpy(&obuCfg.macID, frame.constData(), 4);
        obuCfg.obuStatus=frame[32];

        ObuInfo newObu;
        IccInfo newIcc;
        bool bRlt=newObu.setObuInfo((const quint8 *)frame.constData()+5, 27, (const quint8 *)&obuCfg);
        if (!bRlt)
        {
            logger()->info(tr("trans obu info error, app[%1].")
                           .arg(frame.toHex().data()));
            return;
        }
        setStatus(DEV_STATUS_OK);

        // Note: 如果不回应，RSU每秒重发一次B2
        //       如果回复C1，RSU每隔2.5秒重发一次B2（在没有PSAM卡无法继续发送B3的情况下）
        //       如果回复C2，RSU每隔0.034秒重发一次B2
        m_rsuComm.sendCmd(0xC1, newObu.getMacId());
        emit obuDetected(newObu, newIcc);
    }
}
